#!/usr/bin/env python
# -*- coding: utf-8 -*-

from typing import Any

from cta.config.cross_section_config.multi_factor_config import MultiFactorConfig
from cta.interface.action.open_position.abstract_open_position_action import AbstractOpenPositionAction
from web.constants.datetime_format import DatetimeFormat
from web.constants.direction import Direction
from web.domain.responsibility_chain_dto import ResponsibilityChainDto
from web.manager.log_manager import LogManager
from web.models import CommodityFutureInfo
from web.models.commodity_future_date_contract_data import CommodityFutureDateContractData
from web.models.optimize2_account import Optimize2Account
from web.models.optimize2_commodity_future_transaction_record import Optimize2CommodityFutureTransactionRecord
from web.models.quant2_commodity_future_transaction_record import Quant2CommodityFutureTransactionRecord
from web.util.commodity_future_code_util import CommodityFutureCodeUtil
from web.util.fee_util import FeeUtil

Logger = LogManager.get_logger(__name__)

class Optimize2MultiFactorOpenPositionAction(AbstractOpenPositionAction):
    """
    多因子策略，开仓期货
    """

    def exec(self, responsibility_chain_dto: ResponsibilityChainDto = None) -> Any:
        Logger.info("多因子策略，开仓期货")

        transaction_date: str = responsibility_chain_dto.transaction_date

        # 根据持仓时间，判断k线形态；判断当天是否应当平仓和开仓
        k_line_movement_pattern, close_or_open_position = self.optimize2_k_line_movement_pattern_and_close_or_open_position_by_holding_position_time(transaction_date)
        if not close_or_open_position:
        # if not self.close_or_open_position_by_up_down_percentage_with_n_date(transaction_date):
            Logger.info("日期[%s]不应当平仓和开仓", transaction_date)
            super().next(responsibility_chain_dto)
            return

        # 计算多头仓位和空头仓位应该是多少
        # should_long_position_number, should_short_position_number = self.calculate_long_position_and_short_position(quant2_commodity_future_transaction_record_list)
        # Logger.info("多头应该的仓位为[%s]，空头应该的仓位为[%s]", should_long_position_number, should_short_position_number)

        # 查询所有账号
        optimize2_account_list: list[Optimize2Account] = self.optimize2_account_dao.find_all()
        if optimize2_account_list is not None and len(optimize2_account_list):
            for optimize2_account in optimize2_account_list:

                # 持仓量
                hold_commodity_future_number: int = optimize2_account.hold_commodity_future_number

                # 根据账号，查询当天新开仓的期货，并且在表optimize2_c_f_transact_record表中没有持仓的记录
                quant2_commodity_future_transaction_record_list: list[Quant2CommodityFutureTransactionRecord] = self.quant2_commodity_future_transaction_record_dao.find_current_open_position_and_not_hold_in_optimize2_commodity_future_transaction_record_by_date_account_name(transaction_date, optimize2_account.account_name)

                # 如果某个账号没有需要开仓的期货，则跳过
                if quant2_commodity_future_transaction_record_list is None or len(quant2_commodity_future_transaction_record_list) == 0:
                    Logger.info("账号[%s]没有需要开仓的期货", optimize2_account.account_name)
                    continue

                # # 查询实际持仓期货中多头数量
                # actual_long_position_number: int = self.find_optimize2_actual_long_position_number(optimize2_account.account_name, Direction.Up)
                # # 查询实际持仓期货中空头数量
                # actual_short_position_number: int = self.find_optimize2_actual_short_position_number(optimize2_account.account_name, Direction.Down)
                # # 总持仓数量
                # total_hold_commodity_future_number: int = hold_commodity_future_number
                #
                # # 如果某个账号的持有期货的数量已经到达最大持有期货的数量，则跳过这个账号
                # if actual_long_position_number >= should_long_position_number \
                #         or actual_short_position_number >= should_short_position_number \
                #         or total_hold_commodity_future_number >= MultiFactorConfig.Max_Hold_Commodity_Future_Number:
                #     Logger.info("账号[%s]的持有期货数量已经到达[%d]只，不再开仓期货", optimize2_account.account_name, total_hold_commodity_future_number)
                #     continue

                for quant2_commodity_future_transaction_record in quant2_commodity_future_transaction_record_list:

                    # 如果没有到达最大持仓量，则继续开仓
                    if hold_commodity_future_number < MultiFactorConfig.Max_Hold_Commodity_Future_Number:
                        optimize2_commodity_future_transaction_record: Optimize2CommodityFutureTransactionRecord = Optimize2CommodityFutureTransactionRecord()
                        optimize2_commodity_future_transaction_record.code = quant2_commodity_future_transaction_record.code
                        optimize2_commodity_future_transaction_record.account_name = quant2_commodity_future_transaction_record.account_name
                        optimize2_commodity_future_transaction_record.direction = quant2_commodity_future_transaction_record.direction
                        optimize2_commodity_future_transaction_record.k_line_movement_pattern = k_line_movement_pattern

                        # 查询commodity_future_info记录（大小写不敏感的精确匹配查询）
                        filter_dict = {'code__iexact': CommodityFutureCodeUtil.code_to_contract_code(quant2_commodity_future_transaction_record.code)}
                        commodity_future_info: CommodityFutureInfo = self.commodity_future_info_dao.find_one(filter_dict, dict(), list())

                        # 查询期货记录
                        filter_dict = {'code': quant2_commodity_future_transaction_record.code, 'transaction_date': transaction_date}
                        commodity_future_date_contract_data: CommodityFutureDateContractData = self.commodity_future_date_contract_data_dao.find_one(filter_dict, dict(), list())
                        if commodity_future_date_contract_data is None:
                            Logger.warning("期货在日期[%s]没有交易记录，从表quant2_c_f_filter表中删除这条记录，跳过这个期货", quant2_commodity_future_transaction_record.code, transaction_date)

                            continue

                        if quant2_commodity_future_transaction_record.direction == Direction.Up:

                            # 如果期货的收盘价过高，或者资金资产太少，连一手也无法开仓，则直接查找下一个账号
                            if FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, 1) > optimize2_account.capital_assets:
                                break

                            # 如果可以开仓，则向optimize2_c_f_transact_record表中插入数据。
                            # 计算应该开仓期货的数量
                            # quant2_c_f_filter表的记录数（up_down_percentage_with_n_date字段大于等于0）
                            # quant2_c_f_filter_number: int = -1
                            # filter_dict = {'up_down_percentage_with_n_date__gte': 0}
                            # quant2_c_f_filter_number = len(self.quant2_commodity_future_filter_dao.find_list(filter_dict, dict(), dict()))
                            #
                            # # 应当开仓的期货的数量
                            # should_open_position_commodity_future_number: int = -1
                            # if quant2_c_f_filter_number <= (should_long_position_number - actual_long_position_number):
                            #     should_open_position_commodity_future_number = quant2_c_f_filter_number
                            # else:
                            #     should_open_position_commodity_future_number = should_long_position_number - actual_long_position_number
                            #
                            # # 判断是否需要继续开仓
                            # if should_open_position_commodity_future_number == 0:
                            #     Logger.info("账号[%s]持有多头期货的数量已经是%d，不再需要开仓期货", optimize2_account.account_name, actual_long_position_number)
                            #     break

                            # 计算开仓多少手
                            # 开仓多少手
                            init_open_or_close_position_lot: int = FeeUtil.calculate_open_commodity_future_lot(commodity_future_date_contract_data.close_price, commodity_future_info, float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number))
                            open_or_close_position_lot: int = init_open_or_close_position_lot if init_open_or_close_position_lot > 1 else 1
                            while float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) >= FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot):
                                if FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot) >= float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number):
                                    break
                                open_or_close_position_lot = open_or_close_position_lot + 1
                            if float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) < FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot):
                                if open_or_close_position_lot == 1:
                                    continue
                                else:
                                    open_or_close_position_lot = open_or_close_position_lot - 1

                            optimize2_commodity_future_transaction_record.buy_date = quant2_commodity_future_transaction_record.buy_date
                            optimize2_commodity_future_transaction_record.buy_price = quant2_commodity_future_transaction_record.buy_price
                            optimize2_commodity_future_transaction_record.buy_lot = open_or_close_position_lot
                            optimize2_commodity_future_transaction_record.open_commission = FeeUtil.calculate_open_commodity_future_fee(quant2_commodity_future_transaction_record.buy_price, commodity_future_info, open_or_close_position_lot)

                        if quant2_commodity_future_transaction_record.direction == Direction.Down:
                            # 如果期货的收盘价过高，或者资金资产太少，连一手也无法开仓，则直接查找下一个账号
                            if FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, 1) > optimize2_account.capital_assets:
                                break

                            # 如果可以开仓，则向quant2_c_f_transact_record表中插入数据，从quant2_c_f_filter表中删除这条记录。
                            # 计算应该开仓期货的数量
                            # quant2_c_f_filter表的记录数（up_down_percentage_with_n_date字段小于0）
                            # quant2_c_f_filter_number: int = -1
                            # filter_dict = {'up_down_percentage_with_n_date__lt': 0}
                            # quant2_c_f_filter_number = len(self.quant2_commodity_future_filter_dao.find_list(filter_dict, dict(), dict()))
                            #
                            # # 应当开仓的期货的数量
                            # should_open_position_commodity_future_number: int = -1
                            # if quant2_c_f_filter_number <= (should_short_position_number - actual_short_position_number):
                            #     should_open_position_commodity_future_number = quant2_c_f_filter_number
                            # else:
                            #     should_open_position_commodity_future_number = should_short_position_number - actual_short_position_number
                            #
                            # # 判断是否需要继续开仓
                            # if should_open_position_commodity_future_number == 0:
                            #     Logger.info("账号[%s]持有期货的数量已经是%d，不再需要开仓期货", optimize2_account.account_name, should_short_position_number)
                            #     break

                            # 计算开仓多少手
                            # 开仓多少手
                            init_open_or_close_position_lot: int = FeeUtil.calculate_open_commodity_future_lot(commodity_future_date_contract_data.close_price, commodity_future_info, float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number))
                            open_or_close_position_lot: int = init_open_or_close_position_lot if init_open_or_close_position_lot > 1 else 1
                            while float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) >= FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot):
                                if FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot) >= float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number):
                                    break
                                open_or_close_position_lot = open_or_close_position_lot + 1
                            if float(optimize2_account.capital_assets) / float(MultiFactorConfig.Max_Hold_Commodity_Future_Number - optimize2_account.hold_commodity_future_number) < FeeUtil.calculate_open_commodity_future_cost_and_fee(commodity_future_date_contract_data.close_price, commodity_future_info, open_or_close_position_lot):
                                if open_or_close_position_lot == 1:
                                    continue
                                else:
                                    open_or_close_position_lot = open_or_close_position_lot - 1

                            optimize2_commodity_future_transaction_record.sell_date = quant2_commodity_future_transaction_record.sell_date
                            optimize2_commodity_future_transaction_record.sell_price = quant2_commodity_future_transaction_record.sell_price
                            optimize2_commodity_future_transaction_record.sell_lot = open_or_close_position_lot
                            optimize2_commodity_future_transaction_record.open_commission = FeeUtil.calculate_open_commodity_future_fee(quant2_commodity_future_transaction_record.sell_price, commodity_future_info, open_or_close_position_lot)

                        # 插入记录
                        self.optimize2_commodity_future_transaction_record_dao.save(optimize2_commodity_future_transaction_record)

                        hold_commodity_future_number = hold_commodity_future_number + 1
                    else:
                        Logger.info("账号[%s]的持仓数量已经到达[%s]，不用再开仓", optimize2_account.account_name, MultiFactorConfig.Max_Hold_Commodity_Future_Number)
                        break
        else:
            Logger.error("表quant2_account为空")

        super().next(responsibility_chain_dto)